package org.gcb.core.model;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.gcb.core.model.util.Utilities;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

/**
 * Four variables
 * <dl>
 * <dt>
 * <b>SO</b> - Source Object
 * </dt>
 * <dd>
 * Where this MiscModifier came from
 * </dd>
 * <dt>
 * <b>SP</b> - Source Property
 * </dt>
 * <dd>
 * ??
 * </dd>
 * <dt>
 * <b>TO</b> - Target Object
 * </dt>
 * <dd>
 * What this MiscModifier affects
 * </dd>
 * <dt>
 * <b>TP</b> - Target Property
 * </dt>
 * <dd>
 * What specific value this MiscModifier is modifying.
 * </dd>
 * </dl>
 * @author Jason
 */
public class MiscModifier<SO, TO> {
    BeanWrapper srcBean;
    BeanWrapperImpl trgtBean;
    private final String targetBeanPath;
    private final String sourceBeanPath;
    private SO sourceObject;
    private TO type;
    private String description;

    public MiscModifier(TO type, String sourceBeanPath, String targetBeanPath) {
        this(null, type, sourceBeanPath, targetBeanPath, "[None]");
    }

    public MiscModifier(TO type, String sourceBeanPath, String targetBeanPath, String description) {
	this(null, type, sourceBeanPath, targetBeanPath, description);
    }

    public MiscModifier(SO source, TO targetObject, String sourceBeanPath, String targetBeanPath) {
	this(source, targetObject, sourceBeanPath, targetBeanPath, "[None]");
    }

    public MiscModifier(SO source, TO targetObject, String sourceBeanPath, String targetBeanPath, String description) {
	trgtBean = new BeanWrapperImpl(source);
	srcBean = new BeanWrapperImpl(targetObject);
	this.sourceObject = source;
	this.type = targetObject;
	this.description = description;
	this.sourceBeanPath = sourceBeanPath;
	this.targetBeanPath = targetBeanPath;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public TO getType() {
        return type;
    }

    public void setType(TO type) {
        this.type = type;
    }

    public Integer getValue() {
	if(srcBean==null)
	    return 0;
	if (sourceObject==null) {
	    return 0;
	}
	if (sourceBeanPath.isEmpty()) {
	    return 0;
	}
	Object rst = srcBean.getPropertyValue(sourceBeanPath);
	Class<?> clazz = srcBean.getPropertyType(sourceBeanPath);
	if (clazz.isAssignableFrom(Integer.class)) {
	    return Integer.class.cast(rst);
	}
	return 0;
    }

    public String getSourceBeanPath() {
	return sourceBeanPath;
    }

    public String getTargetBeanPath() {
	return targetBeanPath;
    }
    public static void main(String[] args) {
	Test test = new Test();
	System.out.println("result: "+test.result1);
	System.out.println("result: "+test.result2);
    }
}

class Test {
    Integer result1;
    Integer result2;
    {
	Set<MiscModifier<?, ?>> repo = new HashSet<MiscModifier<?, ?>>(0);
	Buff myBuff = new Buff();
	myBuff.getEffect().setBonus(10);
	Ability myAbility = null;
	MiscModifier<Ability, Skill> test1 = new MiscModifier<Ability, Skill>(new Skill(AbilityType.CHA, "Looks", null, false), Skill.PROP_MODIFIERS, null);
	MiscModifier<Ability, Skill> test2 = new MiscModifier<Ability, Skill>(new Skill(AbilityType.CHA, "Looks", null, false), Skill.PROP_MODIFIERS, null);
	MiscModifier<Buff, Ability> test3 = new MiscModifier<Buff, Ability>(myBuff, myAbility, "effect.bonus", Ability.PROP_SCORE, "Cuz I can.");
	repo.add(test1);
	repo.add(test2);
	repo.add(test3);

	Collection<MiscModifier<?, ?>> mySet = Utilities.retrieveType(Skill.class, repo);
	Collection<MiscModifier<?, ?>> mySet2 = Utilities.retrieveType(Ability.class, repo);
	//Utilities.calculateMiscModifier(mySet);
	result1 = calculateMiscModifier(Skill.PROP_MODIFIERS, mySet);
	result2 = calculateMiscModifier(Ability.PROP_SCORE, mySet2);
    }

    public static Integer calculateMiscModifier(String path, Collection<MiscModifier<?, ?>> modifiers) {
        Integer retVal = 0;
        for (MiscModifier<?, ?> miscMod : modifiers) {
	    if(miscMod.getTargetBeanPath().equals(path))
		retVal += miscMod.getValue();
        }
        return retVal;
    }

//    private void calculateMiscModifier(Collection<MiscModifier<?, Skill>> mySet) {
//	throw new UnsupportedOperationException("Not yet implemented");
//    }
}

class Buff {
    private Effect effect = new Effect();

    public Effect getEffect() {
	return effect;
    }
    public void setEffect(Effect effect) {
	this.effect = effect;
    }
}

class Effect {
    private Integer bonus;

    public Integer getBonus() {
	return bonus;
    }
    public void setBonus(Integer bonus) {
	this.bonus = bonus;
    }
}